/**
 * 基于《canvas 核心技术》一书中对于向量的描述，
 * 实现一个向量类
 * 向量有 大小 和 方向 两个概念。
 */
if(!window.Vector) {

window.Vector = (function () {
    /**
     * 向量类的构造函数
     * @param {Number} x x轴属性
     * @param {Number} y y轴属性
     */
    function Vector(x, y) {
        this.x = x || 0;
        this.y = y || 0;
    }

    /* ====================静态方法==================== */
    /**
     * 计算两个向量之和，并返回新向量 
     * @param {Vector} v1 向量1
     * @param {Vector} v2 向量2
     * @return {Vector} 
     * 几何意义：一个位于原始向量夹角之间的，大小为分量之和的新向量
     */
    Vector.add = function(v1, v2) {
        return new Vector(v1.x + v2.x, v1.y + v2.y);
    }

    /**
     * 计算两个向量之差，并返回新向量 （）
     * @param {Vector} v1 向量1
     * @param {Vector} v2 向量2
     * @return {Vector} 差向量
     * 几何意义：一个从原点出发，平行于原始向量终点连线的，大小为分量之差的新向量
     */
    Vector.sub = function(v1, v2) {
        return new Vector(v1.x - v2.x, v1.y - v2.y);
    }

    /**
     * 计算两个向量的点积结果并返回
     * @param {Vector} v1 向量1
     * @param {Vector} v2 向量2
     * @return {Number} 点积结果
     * 几何意义：点积是标量，其重要意义在于它与0的大小关系：
     *      > 0：说明两个向量方向大概一致
     *      < 0：说明两个向量方向大概相反
     *      = 0：说明两个向量方向刚好相反、且大小相同
     */
    Vector.DotProduct = function(v1, v2) {
        return v1.x * v2.x + v1.y * v2.y;
    }

    /**
     * 克隆指定向量并缩放后返回
     * @param {Vector} v 要缩放的向量
     * @param {Number} s 缩放系数
     */
    Vector.scale = function (v, s) {
        return v.clone().scale(s);
    }

    /**
     * 横纵坐标值在[-1, 1)范围内随机取值的新矢量对象
     * @return {Vector} 随机横纵坐标的新矢量实例
     */
    Vector.random = function () {
        return new Vector(
            Math.random() * 2 - 1,
            Math.random() * 2 - 1
        )
    }


    /* ====================原型对象==================== */
    Vector.prototype = {
        /**
         * 给入一个向量实例，或者x、y分量值，设置对应分量，并返回实例本身
         * @param {Vector, Number} vx 可以是Vector类的实例，也可以是一个数值，用于设置当前实例的x轴分量
         * @param {Number} y 如果x是Number类型，则y有意义，且为Number，设置当前实例的y轴分量
         * @return {Vector} this 返回当前实例
         * 几何意义：以赋值的形式，改变了主调向量的 方向 和 模;
         */
        set: function (vx, y) {
            if(typeof vx === 'object') {
                y = vx.y;
                vx = vx.x;
            }
            this.x = vx || 0;
            this.y = y || 0;
            return this;
        },

        /**
         * 为当前实例加上一个向量
         * @param {Vector} v 作为加数的Vector实例
         * @return {Vector} 当前实例
         * 几何意义：以加法的形式，改变了主调向量的 方向 和 模；
         */
        add: function (v) {
            this.x += v.x;
            this.y += v.y;
            return this;
        },

        /**
         * 当前实例减去一个向量
         * @param {Vector} v 作为减数的Vector实例
         * @return {Vector} 当前实例
         * 几何意义：以减法的形式改变了主调向量的 方向 和 模；
         */
        sub: function (v) {
            this.x -= v.x;
            this.y -= v.y;
            return this;
        },

        /**
         * 当前向量分量数值缩放
         * @param {Number} s 缩放系数
         * @return {Vector} 当前向量
         * 几何意义：通过缩放分量，改变主调向量的 模，方向不变
         */
        scale: function (s) {
            this.x *= s;
            this.y *= s;
            return this;
        },

        /**
         * 计算当前向量的模
         * @return {Number} 当前向量的模
         * 几何意义：向量的模（模：向量的大小）
         * 物理意义：速度、位移等矢量概念的标量表示
         */
        mode: function () {
            return Math.sqrt(this.x * this.x + this.y * this.y);
        },

        /**
         * 分量的平方和
         * @return {Number} 当前向量两个分量的平方和
         * 意义：很多时候会数学计算中会用到这个数值，我们抛出一个方法可以直接获取
         */
        sumOfSq: function () {
            return this.x * this.x + this.y * this.y;
        },

        /**
         * @param {Vector} v 被减向量
         * @return {Number} 两向量的差向量的模
         * 几何意义：是两个坐标点的距离
         */
        modeTo: function (v) {
            var dx = v.x - this.x,
                dy = v.y - this.y;
            return Math.sqrt(dx * dx + dy * dy);
        },

        /**
         * 和指定向量的差向量的分量平方和
         * @param {Vector} v 被减向量
         * @return {Number} 差向量的分量的平方和
         */
        sumOfModeToSq: function (v) {
            var dx = v.x - this.x,
                dy = v.y - this.y;
            return dx * dx + dy * dy;
        },

        /**
         * 单位向量
         * @return {Object} result 结构：{x: cos(a), y:sin(a)}，其中a为向量与水平轴夹角度数
         */
        normalize: function () {
            var m = Math.sqrt(this.x * this.x + this.y * this.y);
            var result = {};
            if(m) {
                result.x /= m;
                result.y /= m;
            }
            return result;
        },

        /**
         * 计算水平轴夹角弧度数
         * @return {Number} 通过反正切计算当前向量与水平轴夹角的弧度数
         */
        angle: function () {
            return Math.atan2(this.y, this.x);
        },

        /**
         * 与指定向量夹角的弧度数
         * @param {Vector} v 同原点的另一个向量
         * @return {Number} 两个同原点的向量夹角的弧度数
         */
        angleTo: function (v) {
            var dx = v.x - this.x,
                dy = v.y - this.y;
            return Math.atan2(dy, dx);
        },

        /**
         * @param {Vector} v 被减向量
         * @param {Number} s 缩放系数
         */
        lerq: function (v, s) {
            this.x += (v.x - this.x) * s;
            this.y += (v.y - this.y) * s;
            return this;
        },

        /**
         * 克隆
         */
        clone: function () {
            return new Vector(this.x, this.y);
        }
    }

    return Vector;
})();

} else {
    throw new Error('Vector被占用');
}